#ifndef MODEL_H
#define MODEL_H

#include <vector>
#include <string>
#include <iostream>
#include <cmath>
#include <fstream>
#include <map> 

#include "utility.h"
#include "mesh.h"
#include "operator.h"
#include "collide.h"


//const float DEG2RAD = 3.141593f / 180;
const float FOV_Y = 60.0f;
//Initial values to set frustrum
const float INIT_LEFT = -1.0f;
const float INIT_RIGHT = 1.0f;
const float INIT_BOTTOM = -1.0f;
const float INIT_TOP = 1.0f;
const float INIT_NEAR = 0.0001f;
const float INIT_FAR = 100.0f;
//Initial values to set camera
const float CAMERA_AGL_X = 45.0f;     // pitch 
const float CAMERA_AGL_Y = -45.0f;    // heading 
const float CAMERA_DIST = -3.5f;    

enum ViewMode {EDIT, VIEW};
enum LeftButtonFunction {TRANSLATE, ROTATE, PAINT} ;

class ViewParameter
{
public:
	ViewParameter():mouseLeftDown(false), mouseRightDown(false), windowSizeChanged(true), updated(true),
		cameraAglX (CAMERA_AGL_X), cameraAglY(CAMERA_AGL_Y), cameraDis (CAMERA_DIST) , 
		projectionLeft(INIT_LEFT), projectionRight(INIT_RIGHT), 
		projectionBottom(INIT_BOTTOM) , projectionTop(INIT_TOP),
		projectionNear (INIT_NEAR), projectionFar(INIT_FAR), cameraX(0.0f), cameraY (0.0f),
		mouseX(0), mouseY(0), viewMode(VIEW), lbf(TRANSLATE){}
public:
	int windowWidth;
	int windowHeight;
	bool windowSizeChanged;

	bool mouseLeftDown;
	bool mouseRightDown;
	int mouseX;
	int mouseY;

	float cameraAglX;
	float cameraAglY;
	float cameraDis ;
	float cameraX ;
	float cameraY ;

	float projectionLeft;
	float projectionRight;
	float projectionBottom;
	float projectionTop;
	float projectionNear;
	float projectionFar;

	double modelViewMatrix[16];
	double projectionMatrix[16];  
	int viewPort[4];

	bool updated;
	ViewMode viewMode; 
	LeftButtonFunction lbf ;
	//bool drawModeChanged;
	//int drawMode;
	//int projectionMode;
} ;


class Model
{
public:
	Model():cullingState (true),uvexpand(NULL) {} 

	Model (const char * fileName) :cullingState (true), uvexpand(NULL), 
		pCullResult(NULL)/*: mouseLeftDown(false), mouseRightDown(false), windowSizeChanged(true), updated (true),
		cameraAglX (CAMERA_AGL_X), cameraAglY(CAMERA_AGL_Y), cameraDis (CAMERA_DIST) , 
		projectionLeft(INIT_LEFT), projectionRight(INIT_RIGHT), 
		projectionBottom(INIT_BOTTOM) , projectionTop(INIT_TOP),
		projectionNear (INIT_NEAR), projectionFar(INIT_FAR) */  
	{
		loadModel (fileName);
		collide = new Collide(& this->mesh);

		//set the sampling brush is a little bit larger than 
		// the paint brush 
		brush.set_size(0.001);
		map_brush.set_size(0.0007);
	}
	~Model() 
	{
		//Remove the culling Result ;
		if (pCullResult) delete pCullResult ;
		delete collide;
	}
public:
	int loadModel (const char * fileName)
	{
		std::ifstream ifs (fileName);
		ifs >> mesh; 
		ostr.open("out.log") ;
		ostr<< mesh.v_faces.size()<<std::endl;
		/*Random face color*/
		srand(10);
		rand_face_color();
		return 0;
	}
	int saveModel (const char * fileName)
	{
		return 0; 
	}

	void init()
	{
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		//glViewport(0, 0, 500, 600);
		glShadeModel(GL_SMOOTH);
		glClearColor(0, 0, 0, 1);   
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_CULL_FACE);
		glEnable(GL_BLEND);
		glEnable(GL_SCISSOR_TEST);
		// track material ambient and diffuse from surface color, call it before glEnable(GL_COLOR_MATERIAL)

		
		//glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
		//glEnable(GL_COLOR_MATERIAL);

		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);   
		glClearStencil(0);                              // clear stencil buffer
		glClearDepth(1.0f);                             // 0 is near, 1 is far
		glDepthFunc(GL_LEQUAL);

		// set up light colors (ambient, diffuse, specular)

				
		//GLfloat mat_amb_diff[] = { 0.1, 0.5, 0.8, 1.0 };
		//glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_amb_diff);

		GLfloat lightAbmient[] = {0.2f, 0.2f, 0.2f, 1.0f};     
		GLfloat lightDiffuse[] = {.9f, .9f, 0.9f, .9f};     
		GLfloat lightSpecular[] = {0.0f, 0.0f, 0.0f, 1.0f};     
		GLfloat lightPos[4] = {0, 20, 20, 1};
		//GLfloat spot_direction[] = { 0, 0, 1 };


		glLightfv(GL_LIGHT0, GL_AMBIENT, lightAbmient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
		//glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
		glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
		//glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
		glEnable(GL_LIGHT0); 
		
		/*
		GLfloat light1_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
		GLfloat light1_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
		GLfloat light1_specular[] = { 1.0, 1.0, 1.0, 1.0 };
		GLfloat light1_position[] = { 10, 10, 10, 0};
		GLfloat spot_direction[] = { 0, 0, -1 };

		glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);
		glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
		glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);
		glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
		glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_direction);

		glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.5);
		glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.5);
		glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.2);
		glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 180.0);
		
		glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2.0);
		glEnable(GL_LIGHT1); 
		*/

		glEnable(GL_LIGHTING);

		int drawMode = 0 ;
		if(drawMode == 0)           // fill mode
		{
			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
			glEnable(GL_DEPTH_TEST);
			glEnable(GL_CULL_FACE);
		}
		else if(drawMode == 1)      // wireframe mode
		{
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			//glDisable(GL_DEPTH_TEST);
			glDisable(GL_CULL_FACE);

			//glEnable(GL_DEPTH_TEST);
			//glEnable(GL_CULL_FACE);
		}
		else if(drawMode == 2)      // point mode
		{
			glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
			//glDisable(GL_DEPTH_TEST);
			glDisable(GL_CULL_FACE);
		}
	}
	void paint()
	{
		map_brush.set_color(int_RGB(255,0,0));
		unsigned count = 0;
		for (unsigned i = 0;i < uvexpand->v_faces.size(); ++i)
		{
			Point2 p[3];
			uvexpand->Face2TexCoord(uvexpand->v_faces[i], p);
			Face * modelFace = uvexpand->v_faces[i] ;
		
			for (unsigned j = 0; j < modelFace->samples.size() ; ++j)
			{
				//map j to barycentric coords
				unsigned x, y;
				unsigned res = modelFace->resolution ;
				index2barycentric(res, j, x, y);
				Point3 point = p[0] * (float (x) / res)   + p[1] * (float(y) / res )
					+ p[2] * (1.0f -float(x+y) /res ) ;
				float ratio; 
				if (map_brush.isInBrush(point, ratio))
				{
					float color_ratio = linear_gaussian_blur(ratio);
					modelFace->samples[j] = map_brush.get_color() * (color_ratio ) + modelFace->samples[j]* (1- color_ratio );
					//modelFace->samples[j] = map_brush.get_color() ;
					count ++ ;
				}
			}
		}
		/*
		ostr<<"brush.x "<<map_brush.get_winx()<<std::endl;
		ostr<<"brush.y "<<map_brush.get_winy()<<std::endl;
		ostr<<"brush.size "<<map_brush.get_pixel_size()<<std::endl;
		ostr<<"Brush mv  ";
		for (int i = 0; i < 16; ++i)
		{
			ostr<<map_brush.modelViewMatrix[i]<<' ';
		}
		ostr<<std::endl;
		ostr<<"Brush proj  ";
		for (int i = 0; i < 16; ++i)
		{
			ostr<<map_brush.projectionMatrix[i]<<' ';
		}
		ostr<<std::endl;

		ostr<<"Brush win  ";
		for (int i = 0; i < 4; ++i)
		{
			ostr<<map_brush.viewPort[i]<<' ';
		}
		ostr<<std::endl;
		ostr<<"Paint samples "<<count<<std::endl<<std::endl;
		*/
		
	}
	void make_frustum(double fovY, double aspectRatio, double front, double back)
	{
		const double DEG2RAD = 3.14159265 / 180;

		double tangent = tan(fovY/2 * DEG2RAD);   // tangent of half fovY
		double height = front * tangent;          // half height of near plane
		double width = height * aspectRatio;      // half width of near plane

		// params: left, right, bottom, top, near, far
		glFrustum(-width, width, -height, height, front, back);
	}
	Face * pick_face ()
	{
		Point3 p1, p2;
		brush.pick_ray(p1, p2);
		std::vector<Face * > faces ;
		collide->segmentCollide(p1, p2, faces);

		for (unsigned i = 0; i < faces.size(); ++i)
		{
			Point3 p[3];
			faces[i]->Face2VertexCoord(p) ;
			if (brush.isVisible(p))
			{
				faces[i]->selected = ! (faces[i]->selected);
				return faces[i];
			}
		}
		return NULL ;
	}


	void draw_culling_result()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

		//Not every time we need to redraw all the region.
		//For example when we move the brush. 
		//glScissor (s_x, s_y, s_w, s_h);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60, float(500)/600, cullingView.projectionNear, cullingView.projectionFar);

		glMatrixMode(GL_MODELVIEW);

		
		glPushMatrix();
		glTranslatef(0, 0, cullingView.cameraDis);
		glRotatef(cullingView.cameraAglX , 1, 0, 0); // pitch
		glRotatef(cullingView.cameraAglY , 0, 1, 0); // heading
		
		

		glGetIntegerv(GL_VIEWPORT, viewport);	
		glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
		glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);


		//Set the property of brush
		brush.set_pixel_size(130);
		brush.set_winx(250);
		brush.set_winy(300);
		//brush.getCoordEnv();


		drawGrid(10, 1);


		if (cullingState == true)
		{
			if (pCullResult) delete pCullResult ;
			pCullResult = NULL ;
			OPBFSCulling culling(&mesh, &brush); 
			pCullResult = culling; 
			
			//OPLSCM lscm (pCullResult);
			//uvexpand = lscm;
			cullingState = false;
		}
		/*
		for (unsigned i = 0; i < uvexpand->v_faces.size(); ++i)
		{
			this->draw_tex_faces(uvexpand->v_faces[i]);
		}
		*/
		
		for (unsigned i = 0; i < pCullResult->v_faces.size(); ++i)
		{
			this->draw_faces(pCullResult->v_faces[i]);
		}
		

		//This env is used for Translate the scene with mouse. 
		//getCoordEnv(mappingView.modelViewMatrix, mappingView.projectionMatrix, mappingView.viewPort);
		glPopMatrix();

	
		//glPushMatrix();
		glLoadIdentity();
		glTranslatef(0, 0, -0.01);
		brush.draw();	
		//glPopMatrix();
	
		
	}
	void draw_expand_result()
	{
		//this->setUpdated(cullingView, true, 40, 40, WINDOW_WIDTH - 40 * 2, WINDOW_HEIGHT - 80);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

		//Not every time we need to redraw all the region.
		//For example when we move the brush. 
		//

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60, float(500)/600, cullingView.projectionNear, cullingView.projectionFar);

		glMatrixMode(GL_MODELVIEW);

		glPushMatrix();
		glLoadIdentity();
		//View transform
		glRotatef(mappingView.cameraAglY, 0, 0, 1); // Rotate along Z-axis
		glTranslatef(0, 0, mappingView.cameraDis );
		//glTranslatef(0, 0, cullingView.cameraDis + 1.5);

		glTranslatef(-mappingView.cameraX, 0.0f, 0.0f);
		glTranslatef(0.0f, mappingView.cameraY, 0.0f);		
		getCoordEnv(mappingView.modelViewMatrix, mappingView.projectionMatrix, mappingView.viewPort);
		//Draw the grid 
		glPushMatrix();
		glRotatef(-90, 1, 0, 0);
		drawGrid(10, 1);
		glPopMatrix();

		//Set the property of brush

		map_brush.getObjCoordEnv(); 
		
		if (cullingState == true)
		{
			if (pCullResult) delete pCullResult ;
			pCullResult = NULL ;
			OPBFSCulling culling(&mesh, &brush);
			//OPCulling culling(&mesh, mappingView.modelViewMatrix, mappingView.projectionMatrix, mappingView.viewPort);
			pCullResult = culling;

			if(pCullResult->v_faces.size() > 0)
			{
				//expand
				OPLSCM lscm (pCullResult);
				uvexpand = lscm;
				cullingState = false;
				
				Face * centerFace = culling.getCenterTriangle();
				
				//Compute the barycentric coords
				Point3 v_o[3];
				Point3 v_w[3];
				centerFace->Face2VertexCoord(v_o);
				v_w[0] = brush.mappingObj2Win(v_o[0]);
				v_w[1] = brush.mappingObj2Win(v_o[1]);
				v_w[2] = brush.mappingObj2Win(v_o[2]);
				Point3 v_b (brush.get_winx(), brush.get_winy(), 1);

				float matrix[3][3], invmatrix[3][3];
				matrix[0][0] = v_w[0].x ; matrix[0][1] = v_w[1].x ; matrix[0][2] = v_w[2].x ;
				matrix[1][0] = v_w[0].y ; matrix[1][1] = v_w[1].y ; matrix[1][2] = v_w[2].y ;
				matrix[2][0] = 1.0f		; matrix[2][1] = 1.0f	  ; matrix[2][2] = 1.0f		;


				inverse3(matrix, invmatrix);
				Vector3 barycoord = multiply_vector(invmatrix, v_b);



				//Affine transformation
				Point2 v[3];
				centerFace->Face2TexCoord(v);
				//Point2 center = (v[0]+v[1]+v[2]) * float(1.0/3.0);
					Point2 center = v[0] * barycoord.x +v[1] * barycoord.y +v[2] * barycoord.z ;
				Point2 trans;
				float scale ;
				compute_affine(centerFace, center, trans, scale);
				OPAffineTransformation afftrans(uvexpand, trans, scale) ; 
				uvexpand = afftrans ;
				//Update brush postion
				

				//centerFace->Face2TexCoord(v);
				//Point3 center3 = (v[0]+v[1]+v[2]) * float(1.0/3.0);
				Point3 winpos = map_brush.mappingObj2Win(Vector3(center.x, center.y, 0.99));
				
				//map_brush.set_winx(winpos.x);
				//map_brush.set_winy(winpos.y);

				map_brush.set_winx(WINDOW_WIDTH / 2);
				map_brush.set_winy(WINDOW_HEIGHT / 2);
				
				
			}
			else
			{
				uvexpand = pCullResult ;
			}
		}
		for (unsigned i = 0; i < uvexpand->v_faces.size(); ++i)
		{
			this->draw_tex_faces(uvexpand->v_faces[i]);
		}

		//This env is used for Translate the scene with mouse. 
		getCoordEnv(mappingView.modelViewMatrix, mappingView.projectionMatrix, mappingView.viewPort);
		glPopMatrix();

		//Draw Brush here
		
		if (mappingView.viewMode == EDIT)
		{
		
			glPushMatrix();
			glLoadIdentity();
			glTranslatef(0, 0, -0.01);
			map_brush.draw();
			glPopMatrix();
		}
	}
	void draw_perspective () 
	{

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
		// Set up viewing matrices
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60, float(500)/600, cullingView.projectionNear, cullingView.projectionFar);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		glPushMatrix();
		//Camera
		glTranslatef(0, 0, cullingView.cameraDis);
		//glRotatef(-90, 0, 0,1); // heading
		glRotatef(cullingView.cameraAglX , 1, 0, 0); // pitch
		glRotatef(cullingView.cameraAglY , 0, 1, 0); // heading
		//glMultMatrixf(rotMat);

		//This is used by culling 
		glGetIntegerv(GL_VIEWPORT, viewport);	
		glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
		glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);

		drawGrid(10, 1);
		//draw code goes here: draw the visible faces
		OPCulling culling(&mesh, mvmatrix, projmatrix, viewport); 
		PseudoMesh * cullingResult = culling ; 
		unsigned size = cullingResult->v_faces.size();
		for (unsigned i = 0; i < size; ++i)
		{
			draw_faces(cullingResult->v_faces[i]);
		}
		delete cullingResult ;

		getCoordEnv(cullingView.modelViewMatrix, cullingView.projectionMatrix, cullingView.viewPort);
		brush.getObjCoordEnv();
		//map_brush.getCoordEnv();
		glPopMatrix();

		if (cullingView.viewMode == EDIT)
		{
			glPushMatrix();
			glLoadIdentity();
			glTranslatef(0, 0, -0.01);
			brush.draw();	//Pixel radius 130
			glPopMatrix(); 
		}
	}
	void drawGrid(float size, float step)
	{
		// disable lighting
		glDisable(GL_LIGHTING);
		glBegin(GL_LINES);

		glColor3f(0.3f, 0.3f, 0.3f);
		float offset = 0.0f;
		for(float i=step; i <= size; i+= step)
		{
			glVertex3f(-size, offset,  i);   // lines parallel to X-axis
			glVertex3f( size, offset,  i);
			glVertex3f(-size, offset, -i);   // lines parallel to X-axis
			glVertex3f( size, offset, -i);

			glVertex3f( i, offset, -size);   // lines parallel to Z-axis
			glVertex3f( i, offset,  size);
			glVertex3f(-i, offset, -size);   // lines parallel to Z-axis
			glVertex3f(-i, offset,  size);
		}

		// x-axis
		glColor3f(0.5f, offset, 0);
		glVertex3f(-size, offset, 0);
		glVertex3f( size, offset, 0);

		// z-axis
		glColor3f(0,offset,0.5f);
		glVertex3f(0, offset, -size);
		glVertex3f(0, offset,  size);

		glEnd();

		// enable lighting back
		glEnable(GL_LIGHTING);
		glFlush();
	}

	void upSampling ()
	{		
		for (unsigned idx = 0; idx < uvexpand->v_faces.size(); ++ idx)
		{
			if (true == uvexpand->v_faces[idx]->selected)
			{
				Face * face = uvexpand->v_faces[idx] ;
				std::vector<int_RGB> old_samples (face->samples);
				unsigned res = face->resolution ;
				res *= 2;
				face->samples.resize((res+1) * (res+2)/2);
				face->resolution = res;

				for (unsigned i = 0; i <=res; ++i)
				{
					for (unsigned j = 0; j <= res - i ; ++j)
					{
						int k = res - i - j;
						if (i % 2 == 0 && j % 2 == 0 && k % 2 == 0)
						{
							face->samples[barycentric2index(res, i,j)] =
								old_samples[barycentric2index(res/2, i/2,j/2)] ;
							continue;
 						}
						//Fix i, two neighbors are (i,j-1,k+1) and (i, j+1,k-1)
						if (i%2 == 0)
						{
							face->samples[barycentric2index(res, i, j)] =
								old_samples[barycentric2index(res/2, i/2,(j-1)/2)] * 0.5f 
								+ old_samples[barycentric2index(res/2, i/2,(j+1)/2)] * 0.5f;
							continue;
						}
						//Fix j, two neighbors are (i-1,j,k+1) and (i+1, j,k-1)
						if (j%2 == 0)
						{
							face->samples[barycentric2index(res, i, j)] =
								old_samples[barycentric2index(res/2, (i-1)/2,(j)/2)] * 0.5f 
								+ old_samples[barycentric2index(res/2, (i+1)/2,(j)/2)] * 0.5f;
							continue;
						}
						//Fix k, two neighbors are (i-1,j+1,k) and (i+1, j-1,k)
						if (k%2 == 0)
						{
							face->samples[barycentric2index(res, i, j)] =
								old_samples[barycentric2index(res/2, (i+1)/2,(j-1)/2)] * 0.5f 
								+ old_samples[barycentric2index(res/2, (i-1)/2,(j+1)/2)] * 0.5f;
							continue;
						}
					}
				}
			}	
		}
	}
	void downSampling()
	{
		for (unsigned i = 0; i < uvexpand->v_faces.size(); ++i)
		{
			if (true == uvexpand->v_faces[i]->selected)
			{
				Face * face = uvexpand->v_faces[i] ;
				unsigned res = face->resolution ;
				res /= 2;
				for (unsigned i = 0 ; i <= res; ++i)
				{
					for (unsigned j = 0; j <= res - i; ++j)
					{
						face->samples[barycentric2index(res, i,j)] = 
							face->samples [barycentric2index (res * 2, i *2, j * 2)] ;
					}
				}
				face->samples.resize ((res + 1) * (res + 2)/2);
				face->resolution = res;
				
				//Face * modelFace = UVFace2ModelFace(face ); 
				//modelFace->samples = face->samples ;
				//modelFace->resolution = face->resolution ;
			}
		}
	}
	// The following functions are interactive function
	bool getUpdated (ViewParameter & vp) const {return vp.updated ;}
	void setUpdated (ViewParameter & vp, bool status)
	{
		setUpdated (vp, status, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
	}
	void setUpdated (ViewParameter & vp, bool status, int x, int y, int w, int h ) 
	{
		vp.updated = status;
		s_x = x ;
		s_y = y;
		s_w = w ; 
		s_h = h ;
		//glViewport(x, y, w, h);
		glScissor(x, y, w, h);
	}

	void setMousePos(ViewParameter & vp, int x, int y){vp.mouseX = x; vp.mouseY = y;}			//MK_LEFTDOWN
	void setMouseLeft (ViewParameter & vp, bool flag) {vp.mouseLeftDown = true;}
	bool getMouseLeft (ViewParameter & vp) const {return vp.mouseLeftDown ;}
	void setMouseRight (ViewParameter & vp, bool flag) {vp.mouseRightDown = true;}
	bool getMouseRight (ViewParameter & vp) const {return vp.mouseRightDown ;}
	void setWindowSize (ViewParameter & vp, int w, int h ) 
	{
		vp.windowSizeChanged = true;
		vp.windowWidth = w; 
		vp.windowHeight = h;
	}

	void setShader (MeshColorShader * s) {this->shader = s ;}


	// These functions are used to control the camera .
	void setCamera(float posX, float posY, float posZ, float targetX, float targetY, float targetZ)
	{
		//glMatrixMode(GL_MODELVIEW);
		//glLoadIdentity();
		//gluLookAt(posX, posY, posZ, targetX, targetY, targetZ, 0, 1, 0); // eye(x,y,z), focal(x,y,z), up(x,y,z)
	}
	void rotateCamera (ViewParameter & vp, int x, int y)
	{
		vp.cameraAglY -= (vp.mouseX - x) ;
		vp.cameraAglX -= (vp.mouseY - y) ;
		vp.mouseX = x;
		vp.mouseY = y;
	}
	void zoomCamera (ViewParameter & vp, int delta)
	{
		vp.cameraDis += (delta - vp.mouseY) * 0.05f;
		vp.mouseY = delta;
	}
	void translateCamera (ViewParameter & vp, int x, int y)
	{
		double winx, winy, winz;
		gluProject(0.0f, 0.0f, 0.0f, mappingView.modelViewMatrix, 
			mappingView.projectionMatrix, mappingView.viewPort, & winx, & winy, & winz);
		Vector3 oldPos = mappingWin2Obj(Vector3(vp.mouseX, vp.mouseY, winz), mappingView.modelViewMatrix, mappingView.projectionMatrix, mappingView.viewPort);
		Vector3 newPos = mappingWin2Obj(Vector3(x, y, winz), mappingView.modelViewMatrix, mappingView.projectionMatrix, mappingView.viewPort);
		vp.cameraX -= newPos.x - oldPos.x ;
		vp.cameraY += newPos.y - oldPos.y ;
		vp.mouseX = x;
		vp.mouseY = y;
	}

	void movePerpBrush(ViewParameter & vp, int x, int y)
	{
		//vp.mouseX = x;
		//vp.mouseY = y;
		brush.set_winx(x);
		brush.set_winy(y);
		//map_brush.set_winx(x);
		//map_brush.set_winy(y);
	}
	void moveMapBrush(ViewParameter & vp, int x, int y)
	{
		//vp.mouseX = x;
		//vp.mouseY = y;
		map_brush.set_winx(x);
		map_brush.set_winy(y);
	}


protected:

	void rand_face_color()
	{
		for (std::vector<Face *>::iterator iter = mesh.v_faces.begin(); iter!= mesh.v_faces.end(); ++iter)
		{
			(*iter)->color = rand_color(); 
			(*iter)->make_samples(); 
		}
	}
	Vector3 face_normal (Face * face)
	{
		Point point[3];
		point[0] = face->he->from_v->point ;
		point[1] = face->he->next->from_v->point ;
		point[2] = face->he->next->next->from_v->point ;

		Vector v1 = point[1] - point[0];
		Vector v2 = point[2] - point[0];
		Vector normal = v1^v2 ;
		return normal ; 
	}
	void draw_tex_faces (Face * face)
	{
		Point2 p[3];
		face->Face2TexCoord(p);
		Point3 point[3];
		point[0].x = p[0].x ; point[0].z = 0.0f ; point[0].y = p[0].y;
		point[1].x = p[1].x ; point[1].z = 0.0f ; point[1].y = p[1].y;
		point[2].x = p[2].x ; point[2].z = 0.0f ; point[2].y = p[2].y;

		unsigned res = face->resolution ;
		unsigned num_samples = (res + 1) * (res + 2) / 2;
		GLfloat  * color1  = new GLfloat[num_samples * 4] ;
		for (unsigned i = 0; i < num_samples; ++i)
		{
			color1 [4*i + 0] = face->samples[i].r / 255.0f;
			color1 [4*i + 1] = face->samples[i].g / 255.0f;
			color1 [4*i + 2] = face->samples[i].b / 255.0f;
			color1 [4*i + 3] = 1.0f;
		}
		draw_triangles(point, color1, res, Vector3(0,0,1));
		if (face->selected)
		{
			draw_blend_triangles (point,Vector3(0,0,1));
		}
		delete []color1 ;
		glFlush();
	}

	void draw_faces (Face * face)
	{
		unsigned res = face->resolution ;
		unsigned num_samples = (res + 1) * (res + 2) / 2;
		GLfloat  * color1  = new GLfloat[num_samples * 4] ;
		for (unsigned i = 0; i < num_samples; ++i)
		{
				color1 [4*i + 0] = face->samples[i].r / 255.0f;
				color1 [4*i + 1] = face->samples[i].g / 255.0f;
				color1 [4*i + 2] = face->samples[i].b / 255.0f;
				color1 [4*i + 3] = 1.0f;
		}
		Point p[3];
		face->Face2VertexCoord(p);
		Vector3 normal = face->normal();
		draw_triangles(p, color1, res, normal);
		if (face->selected)
		{
			draw_blend_triangles (p, normal);
		}
		delete []color1 ;
	}
	void draw_blend_triangles (Point point[3], Vector3 & normal )
	{
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
		glBegin(GL_TRIANGLES);
		glColor4f(0.75,0.75,0.75,0.75);
		glNormal3f(normal.x, normal.y, normal.z); 
		glVertex3f(point[0].x, point[0].y, point[0].z);
		glVertex3f(point[1].x, point[1].y, point[1].z);
		glVertex3f(point[2].x, point[2].y, point[2].z);
		glEnd();
		glDisable(GL_BLEND);
	}
	void draw_triangles (Point point[3], GLfloat * color , int res, Vector3 & normal)
	{
		GLfloat k, h, c;
		shader->get_tex_coord(point, k, h, c);
		shader->enable_draw_face();
		shader->set_triangle_shader_uniform(point, color, res, k, h, c);

		glBegin(GL_TRIANGLES);
		glNormal3f(normal.x, normal.y, normal.z); 
		glTexCoord2f(0.0, 0.0); glVertex3f(point[0].x, point[0].y, point[0].z);
		glTexCoord2f(c, 0.0); glVertex3f(point[1].x, point[1].y, point[1].z);
		glTexCoord2f(k, h); glVertex3f(point[2].x, point[2].y, point[2].z);
		glEnd();
		glFlush();

		shader->disable_draw_face();
	}

public:
	ViewParameter cullingView;
	ViewParameter mappingView; 
	bool cullingState ;


private:
	Mesh mesh ;
	unsigned face_size ;
	STATES states;	
	std::ofstream ostr ;		// This is used to write log file. 


	Vector3 frustumVertices[8];         // 8 vertices of frustum
	Vector3 frustumNormals[6];          // 6 face normals of frustum
	//GLUquadricObj* quadricId;

	//float bgColor[4];
	//float matrixProjection[16];

	//all these members are used for maintained the glmappingview

	double mvmatrix[16];
	double projmatrix[16];  
	int viewport[4];

	
	MeshColorShader * shader ;
	PseudoMesh * uvexpand ;
	PseudoMesh * pCullResult ;


	Brush brush ;
	Brush map_brush ;
	//Scissor structure ;
	unsigned s_x, s_y, s_w, s_h ;

	Collide  * collide ;
};
#endif